/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0.
 *
 * @Date: 2021-04-07 09:53:07
 * @LastEditTime: 2021-09-02 18:36:03
 * @Description:  This files is for uart functions
 *
 * @Modify History:
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */

/***************************** Include Files ********************************/

#include "pl011_uart.h"
#include "ft_io.h"
#include "ft_error_code.h"

u32 Pl011SendBuffer(Pl011 *uart_p);
u32 Pl011ReceiveBuffer(Pl011 *uart_p);

static void Pl011StubHandler(void *Args, u32 Event,
                             u32 ByteCount);

/**
 * @name: Pl011CfgInitialize
 * @msg: initalize uart configure
 * @return {*}
 * @param {Ft_Uart} *uart_p
 * @param {Pl011Config} *Config
 */
s32 Pl011CfgInitialize(Pl011 *uart_p, Pl011Config *Config)
{
    u32 RegValue = 0;

    FT_ASSERTNONVOID(uart_p != NULL);
    FT_ASSERTNONVOID(Config != NULL);

    uart_p->config.instance_id = Config->instance_id;
    uart_p->config.base_address = Config->base_address;
    uart_p->config.ref_clock_hz = Config->ref_clock_hz;
    uart_p->config.irq_num = Config->irq_num;

    uart_p->handler = Pl011StubHandler;

    uart_p->send_buffer.byte_p = NULL;
    uart_p->send_buffer.requested_bytes = 0;
    uart_p->send_buffer.remaining_bytes = 0;

    uart_p->receive_buffer.byte_p = NULL;
    uart_p->receive_buffer.requested_bytes = 0;
    uart_p->receive_buffer.remaining_bytes = 0;
    uart_p->rxbs_error = 0;
    uart_p->is_ready = FT_COMPONENT_IS_READY;
    //<! 设置波特率

    /*
         * Set up the default data format: 8 bit data, 1 stop bit, no
         * parity
         */
    RegValue = ((FUART_FORMAT_WORDLENGTH_8BIT << 5) & UARTLCR_H_WLEN); //<! 8bit word length
    RegValue |= (0 << 3) & UARTLCR_H_STP2;                             //<! 1 stopbit
    RegValue |= (0 << 1) & UARTLCR_H_PEN;                              //<! no parity
    RegValue |= (1 << 4) & UARTLCR_H_FEN;                              //<! fifo en
    FT_UART_WRITEREG32(uart_p->config.base_address, UARTLCR_H_OFFSET, RegValue);

    /* Set the RX FIFO trigger at 8 data bytes.Tx FIFO trigger is 8 data bytes*/
    RegValue = (1 << 3) | (1 << 0);
    FT_UART_WRITEREG32(uart_p->config.base_address, UARTIFLS_OFFSET, RegValue);

    /* Disable all interrupts, polled mode is the default */
    RegValue = 0;
    FT_UART_WRITEREG32(uart_p->config.base_address, UARTIMSC_OFFSET, RegValue);

    return FT_SUCCESS;
}

/**
 * @name: Pl011Send
 * @msg:
 * @param {Ft_Uart} *uart_p
 * @param {u8} *Buffer
 * @param {u32} Length
 * @return {u32} The Number of bytes actully sent.
 */
u32 Pl011Send(Pl011 *uart_p, u8 *byte_p, u32 Length)
{
    u32 RegValue = 0;
    u32 SentCount = 0;
    FT_ASSERTNONVOID(uart_p != NULL);
    FT_ASSERTNONVOID(byte_p != NULL);
    FT_ASSERTNONVOID(uart_p->is_ready == FT_COMPONENT_IS_READY);

    /*
     * Disable the UART transmit interrupts to allow this call to stop a
     * previous operation that may be interrupt driven.
     */
    RegValue = FT_UART_READREG32(uart_p->config.base_address, UARTIMSC_OFFSET);
    RegValue &= ~(UARTIMSC_TXIM);
    FT_UART_WRITEREG32(uart_p->config.base_address, UARTIMSC_OFFSET, RegValue);

    uart_p->send_buffer.byte_p = byte_p;
    uart_p->send_buffer.requested_bytes = Length;
    uart_p->send_buffer.remaining_bytes = Length;

    SentCount = Pl011SendBuffer(uart_p);

    return SentCount;
}

/**
 * @name: Pl011PutChar
 * @msg: send a char through uart
 * @return {*}
 * @param {Ft_Uart} *uart_p
 * @param {s8} Data
 */
void Pl011PutChar(Pl011 *uart_p, s8 Data)
{
    FT_ASSERTVOID(uart_p != NULL);
    FT_ASSERTVOID(uart_p->is_ready == FT_COMPONENT_IS_READY);

    while (!FT_UART_ISTRANSMITFULL(uart_p->config.base_address))
    {
        FT_UART_WRITEREG32(uart_p->config.base_address, UARTDR_OFFSET, Data);
        break;
    }
}

static void Pl011StubHandler(void *Args, u32 Event,
                             u32 ByteCount)
{
    (void)Args;
    (void)Event;
    (void)ByteCount;

    FT_ASSERTVOIDALWAYS();
}

/**
 * @name: Pl011SendBuffer
 * @msg: send data buffer through uart
 * @return {*}
 * @param {Ft_Uart} *uart_p
 */
u32 Pl011SendBuffer(Pl011 *uart_p)
{
    u32 SentCount = 0U;
    u32 RegValue;

    /*
     * If the TX FIFO is full, send nothing.
     * Otherwise put bytes into the TX FIFO unil it is full, or all of the
     * data has been put into the FIFO.
     */
    while ((!FT_UART_ISTRANSMITFULL(uart_p->config.base_address)) && (uart_p->send_buffer.remaining_bytes > SentCount))
    {
        FT_UART_WRITEREG32(uart_p->config.base_address, UARTDR_OFFSET, (u32)uart_p->send_buffer.byte_p[SentCount]);
        SentCount++;
    }

    /* Update the buffer to reflect the bytes that were sent from it */
    uart_p->send_buffer.byte_p += SentCount;
    uart_p->send_buffer.remaining_bytes -= SentCount;

    RegValue = FT_UART_READREG32(uart_p->config.base_address, UARTIMSC_OFFSET);
    RegValue |= (UARTIMSC_TXIM);
    FT_UART_WRITEREG32(uart_p->config.base_address, UARTIMSC_OFFSET, RegValue);

    return SentCount;
}

/**
 * @name: Pl011Receive
 * @msg: receive data through uart
 * @return {*}
 * @param {Ft_Uart} *uart_p
 * @param {u8} *byte_p
 * @param {u32} Length
 */
u32 Pl011Receive(Pl011 *uart_p, u8 *byte_p, u32 Length)
{
    u32 Received;
    u32 BackRegValue;

    FT_ASSERTNONVOID(uart_p != NULL);
    FT_ASSERTNONVOID(byte_p != NULL);
    FT_ASSERTNONVOID(uart_p->is_ready == FT_COMPONENT_IS_READY);

    /*
     * Disable all the interrupts.
     * This stops a previous operation that may be interrupt driven
     */
    BackRegValue = FT_UART_READREG32(uart_p->config.base_address, UARTIMSC_OFFSET);
    FT_UART_WRITEREG32(uart_p->config.base_address, UARTIMSC_OFFSET, 0);

    uart_p->receive_buffer.byte_p = byte_p;
    uart_p->receive_buffer.requested_bytes = Length;
    uart_p->receive_buffer.remaining_bytes = Length;

    Received = Pl011ReceiveBuffer(uart_p);
    FT_UART_WRITEREG32(uart_p->config.base_address, UARTIMSC_OFFSET, BackRegValue);

    return Received;
}

/**
 * @name: Ft_Uart_ReceiveBuffer
 * @msg: handling uart receive buffer
 * @return {*}
 * @param {Ft_Uart} *uart_p
 */
u32 Pl011ReceiveBuffer(Pl011 *uart_p)
{

    u32 ReceivedCount = 0U;
    u32 Event;
    u32 EventData;
    u32 ByteValue;

    while ((ReceivedCount < uart_p->receive_buffer.remaining_bytes) && !FT_UART_ISRECEIVEDATA(uart_p->config.base_address))
    {
        ByteValue = FT_UART_READREG32(uart_p->config.base_address, UARTDR_OFFSET);

        if (uart_p->rxbs_error)
        {
            if ((ByteValue & UARTDR_ALLE) != 0)
            {
                EventData = ByteValue;
                Event = FUART_EVENT_PARE_FRAME_BRKE;

                if (uart_p->handler)
                {
                    uart_p->handler(uart_p->args, Event, EventData);
                }
            }
        }
        uart_p->receive_buffer.byte_p[ReceivedCount] = (u8)(ByteValue & 0xff);
        ReceivedCount++;
    }

    uart_p->rxbs_error = 0;

    if (uart_p->receive_buffer.byte_p != NULL)
    {
        uart_p->receive_buffer.byte_p += ReceivedCount;
    }
    uart_p->receive_buffer.remaining_bytes -= ReceivedCount;

    return ReceivedCount;
}

/**
 * @name: Pl011BlockSend
 * @msg: initiate uart block send
 * @return {*}
 * @param {Ft_Uart} *uart_p
 * @param {u8} *byte_p
 * @param {u32} Length
 */
void Pl011BlockSend(Pl011 *uart_p, u8 *byte_p, u32 Length)
{
    u32 index;

    FT_ASSERTVOID(uart_p != NULL);
    FT_ASSERTVOID(byte_p != NULL);
    FT_ASSERTVOID(uart_p->is_ready == FT_COMPONENT_IS_READY);

    for (index = 0; index < Length; index++)
    {
        Pl011SendByte(uart_p->config.base_address, byte_p[index]);
    }
}

/**
 * @name: Pl011BlockReceive
 * @msg: initiate uart block receive
 * @return {*}
 * @param {Ft_Uart} *uart_p
 */
u8 Pl011BlockReceive(Pl011 *uart_p)
{
    FT_ASSERTBYTE(uart_p != NULL);
    FT_ASSERTBYTE(uart_p->is_ready == FT_COMPONENT_IS_READY);
    return Pl011RecvByte(uart_p->config.base_address);
}

/**
 * @name: Pl011SetBaudRate
 * @msg: set baudrate of UART trans
 * @return {*}
 * @param {Ft_Uart} *uart_p
 * @param {u32} BaudRate
 */
u32 Pl011SetBaudRate(Pl011 *uart_p, u32 baudrate)
{
    u32 temp;
    u32 divider;
    u32 remainder;
    u32 fraction;

    FT_ASSERTNONVOID(NULL != uart_p);
    if ((baudrate * 2) > uart_p->config.ref_clock_hz)
    {
        return ERR_INPUT_BAUD_NO_SUPPORT;
    }

    /* calculate baud rate divisor */
    temp = 16 * baudrate;
    divider = uart_p->config.ref_clock_hz / temp;
    remainder = uart_p->config.ref_clock_hz % temp;
    temp = (128 * remainder) / temp;
    fraction = temp / 2;

    if (0 != (temp & 1))
    {
        fraction++;
    }

    Pl011ClearSpecificOptions(uart_p, FUART_OPTION_RXEN | FUART_OPTION_TXEN);
    /* set baud register */
    FT_UART_WRITEREG32(uart_p->config.base_address, UARTIBRD_OFFSET, divider);
    FT_UART_WRITEREG32(uart_p->config.base_address, UARTFBRD_OFFSET, fraction);
    Pl011SetSpecificOptions(uart_p, FUART_OPTION_RXEN | FUART_OPTION_TXEN);

    return ERR_SUCCESS;
}

void Pl011IrqClearReciveTimeOut(Pl011 *uart_p)
{
    u32 reg_temp;
    reg_temp = Pl011GetInterruptMask(uart_p);
    reg_temp &= ~UARTMIS_RTMIS;
    Pl011SetInterruptMask(uart_p, reg_temp);
}

void Pl011IrqEnableReciveTimeOut(Pl011 *uart_p)
{
    u32 reg_temp;
    reg_temp = Pl011GetInterruptMask(uart_p);
    reg_temp |= UARTMIS_RTMIS;
    Pl011SetInterruptMask(uart_p, reg_temp);
}
